<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>调度执行</title>
	</head>
	<body>
		<script>
			let activeEffect

      const effectStack = []

			function effect(fn,options = {}) {
				// activeEffect = fn
        // fn()
        const effectFn = () => {
          cleanup(effectFn)
          activeEffect = effectFn
          effectStack.push(effectFn)
          fn()
          effectStack.pop()

          activeEffect = effectStack[effectStack.length - 1]

        }
        effectFn.options = options
        effectFn.deps = []
        effectFn()
			}

      function cleanup (effectFn){
        for(let i=0;i<effectFn.deps.length;i++){
          const deps = effectFn.deps[i]
          deps.delete(effectFn)
        }

        effectFn.deps.length = 0
      }

			const bucket = new WeakMap()

			const track = (target, key) => {
        console.log('%c [ key ]-36', 'font-size:13px; background:pink; color:#bf2c9f;', key);
				if (!activeEffect) {
					return target[key]
				}

				// 1. 获取当前对象对应的依赖集合
				let depsMap = bucket.get(target)
        
				if (!depsMap) {
          depsMap = new Map()
					bucket.set(target, depsMap)
				}

				// 2. 获取当前属性对应的依赖集合
				let deps = depsMap.get(key)
        
				if (!deps) {
          deps = new Set()
					depsMap.set(key, deps)
				}
        
				deps.add(activeEffect)

        activeEffect.deps.push(deps)

			}

			const trigger = (target, key) => {
				// 1. 获取当前对象对应的依赖集合
				const depsMap = bucket.get(target)
				if (!depsMap) return

				// 2. 获取当前属性对应的依赖集合
				const effects = depsMap.get(key)

				// 3. 执行依赖函数
        const effectsToRun = new Set()
        effects && effects.forEach((fn) => {
          if(activeEffect !== fn){ // 避免自己执行自己 无限递归爆栈
            effectsToRun.add(fn)
          }
        })

        effectsToRun && effectsToRun.forEach((effectFn) =>{
          // 如果存在调度器 则调用调度器 把函数作为参数传入
          if(effectFn.options.scheduler){
            effectFn.options.scheduler(effectFn)
          }else{
            effectFn()
          }
        })
			}

      // ********************
      const jobQueue = new Set ()

      const p = Promise.resolve()

      let isFlushing = false

      function flushJob (){
        if(isFlushing)return 
        isFlushing = true
        
        p.then(()=>{
          jobQueue.forEach(job=>job())
        }).finally(()=>{
          isFlushing = false
        })

      }
// ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


			// const data = {ok:true, text: 'hello world' }
      const data = {foo:1}

			const obj = new Proxy(data, {
				get(target, key) {
					track(target, key)

					return target[key]
				},

				set(target, key, newVal) {
					target[key] = newVal
					trigger(target, key)
				},
			})

      // effect(()=>{
      //   console.log('%c [  ]-125', 'font-size:13px; background:pink; color:#bf2c9f;', obj.foo);
      // },{
      //   scheduler(fn){
      //     setTimeout(fn,1000)
      //   }
      // })

      effect(()=>console.log('%c [ ?? ]-129', 'font-size:13px; background:pink; color:#bf2c9f;', obj.foo),{
        scheduler(fn){
          jobQueue.add(fn)
          flushJob()
        }
      })
      obj.foo++
      obj.foo++
      obj.foo++



      console.log('%c [  ]-130', 'font-size:13px; background:pink; color:#bf2c9f;', 'end');

	
		</script>
	</body>
</html>
